package com.zatech.cgnci.project.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zatech.cgnci.project.base.enums.ResponseCodeEnum;
import com.zatech.cgnci.project.base.exception.GlobalException;
import com.zatech.cgnci.project.base.utils.BeanUtils;
import com.zatech.cgnci.project.base.utils.VerifyUtils;
import com.zatech.cgnci.project.dto.ReportCreateDTO;
import com.zatech.cgnci.project.entity.CgnciBusinessInsuranceDO;
import com.zatech.cgnci.project.entity.CgnciManageReportDO;
import com.zatech.cgnci.project.mapper.CgnciManageReportMapper;
import com.zatech.cgnci.project.service.CgnciBusinessInsuranceService;
import com.zatech.cgnci.project.service.CgnciDirectInsuranceService;
import com.zatech.cgnci.project.service.CgnciManageReportService;
import com.zatech.cgnci.project.service.CgnciReinsuranceInfoService;
import com.zatech.cgnci.project.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务经营报表 服务实现类
 * </p>
 *
 * @author Generator
 * @since 2025-02-20
 */
@Service
@Slf4j
public class CgnciManageReportServiceImpl extends ServiceImpl<CgnciManageReportMapper, CgnciManageReportDO> implements CgnciManageReportService {

    @Resource
    CgnciBusinessInsuranceService businessInsuranceService;
    @Resource
    CgnciReinsuranceInfoService reinsuranceInfoService;
    @Resource
    CgnciDirectInsuranceService directInsuranceService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveManageReport(ReportCreateDTO extractDTO) {
        // 查询生效的保单
        List<CgnciBusinessInsuranceDO> businessInsuranceList = businessInsuranceService.list(new LambdaQueryWrapper<CgnciBusinessInsuranceDO>().between(CgnciBusinessInsuranceDO::getStartDate, extractDTO.getInitialDate(), extractDTO.getPeriodDate()).eq(CgnciBusinessInsuranceDO::getBusinessType, "1").eq(CgnciBusinessInsuranceDO::getStatus, "3").eq(CgnciBusinessInsuranceDO::getIsMirror, "0"));
        List<CgnciManageReportDO> manageReportList = new ArrayList<>();
        businessInsuranceList.forEach(insurance -> {
            log.info("保单id:{},正在生成业务经营报表", insurance.getInsuranceId());

            // 查询分入信息
            CgnciReinsuranceInfoVO divideInReinsurance = reinsuranceInfoService.getDivideInReinsuranceByInsuranceId(insurance.getInsuranceId());

            // 查询临分分出信息
            CgnciReinsuranceInfoVO lfDivideOutReinsurance = reinsuranceInfoService.getLfDivideOutReinsuranceByInsuranceId(insurance.getInsuranceId());

            // 查询合约分出信息
            List<CgnciReinsuranceInfoVO> hyDivideOutReinsuranceList = reinsuranceInfoService.listHyDivideOutReinsuranceByInsuranceId(insurance.getInsuranceId());

            if (VerifyUtils.isEmpty(divideInReinsurance)) {
                log.info("保单id:{},没有录入分入信息", insurance.getInsuranceId());
                return;
            }
            List<CgnciReinsuranceRiskVO> divideInRisks = divideInReinsurance.getRisks();
            if (VerifyUtils.isEmpty(divideInRisks)) {
                log.info("保单id:{},没有录入分入的险种信息", insurance.getInsuranceId());
                return;
            }

            // 初始化汇总字段
            BigDecimal maxCompensation = BigDecimal.ZERO;
            BigDecimal premiumInclTax = BigDecimal.ZERO;
            BigDecimal feeRate = BigDecimal.ZERO;
            BigDecimal divideInGrossPremium = BigDecimal.ZERO;
            BigDecimal divideInCommission = BigDecimal.ZERO;
            BigDecimal divideInNetPremium = BigDecimal.ZERO;
            BigDecimal divideOutGrossPremium = BigDecimal.ZERO;
            //BigDecimal stageDivideOutGrossPremium = BigDecimal.ZERO;
            BigDecimal netSelfShare = BigDecimal.ZERO;
            BigDecimal netSelfShareRate = BigDecimal.ZERO;

            AtomicInteger sort = new AtomicInteger(0);
            for (CgnciReinsuranceRiskVO risk : divideInRisks) {
                CgnciManageReportDO manageReportDO = new CgnciManageReportDO();
                manageReportDO.setRegisterId(extractDTO.getRegisterId());
                manageReportDO.setInsuranceId(insurance.getInsuranceId());
                manageReportDO.setProjectId(insurance.getProjectId());
                manageReportDO.setProjectName(insurance.getProjectName());
                manageReportDO.setProjectNo(insurance.getProjectNo());
                manageReportDO.setBusinessYear(insurance.getBusinessYear());
                manageReportDO.setRiskSubType(risk.getRiskSubType());
                manageReportDO.setRiskSubTypeCode(risk.getRiskSubTypeCode());

                // 查询直保信息
                CgnciDirectInsuranceVO directInsurance = directInsuranceService.getDirectInsuranceByBusinessId(insurance.getInsuranceId());
                List<CgnciDirectInsuranceRiskVO> insuranceRisks = directInsurance.getInsuranceRisks();
                // 过滤险种
                CgnciDirectInsuranceRiskVO cgnciDirectInsuranceRiskVO = insuranceRisks.stream().filter(value -> risk.getRiskSubTypeCode().equals(value.getRiskSubTypeCode())).findFirst().orElse(null);
                if (VerifyUtils.isNotEmpty(cgnciDirectInsuranceRiskVO)) {
                    // 险种下的标的信息
                    List<CgnciDirectInsuranceDetailVO> details = cgnciDirectInsuranceRiskVO.getDetails();
                    // 取直保保额
                    manageReportDO.setMaxCompensation(BeanUtils.sumBigDecimalProperty(details, CgnciDirectInsuranceDetailVO::getMaxCompensation));
                    maxCompensation = maxCompensation.add(manageReportDO.getMaxCompensation());
                    // 取直保保费(不含税)
                    manageReportDO.setPremiumInclTax(BeanUtils.sumBigDecimalProperty(details, CgnciDirectInsuranceDetailVO::getPremiumExclTax));
                    premiumInclTax = premiumInclTax.add(manageReportDO.getPremiumInclTax());
                    // 费率= 保费/保额
                    manageReportDO.setFeeRate(manageReportDO.getPremiumInclTax().divide(manageReportDO.getMaxCompensation(), 2, RoundingMode.HALF_UP));
                    feeRate = feeRate.add(manageReportDO.getFeeRate());

                }

                // 分入险种层
                // 获取当前险种所有标的的险种层信息
                List<CgnciReinsuranceRiskInsuredVO> riskInsureds = risk.getRiskInsureds();
                List<CgnciReinsuranceRiskLayerVO> divideInLayers = riskInsureds.stream().map(CgnciReinsuranceRiskInsuredVO::getRiskLayers).flatMap(List::stream).collect(Collectors.toList());
                if (VerifyUtils.isNotEmpty(divideInLayers)) {
                    // 分入毛保费
                    manageReportDO.setDivideInGrossPremium(BeanUtils.sumBigDecimalProperty(divideInLayers, CgnciReinsuranceRiskLayerVO::getGrossPremium));
                    divideInGrossPremium = divideInGrossPremium.add(manageReportDO.getDivideInGrossPremium());

                    // 分入手续费
                    manageReportDO.setDivideInCommission(BeanUtils.sumBigDecimalProperty(divideInLayers, CgnciReinsuranceRiskLayerVO::getCommission));
                    divideInCommission = divideInCommission.add(manageReportDO.getDivideInCommission());
                    // 分入净保费
                    manageReportDO.setDivideInNetPremium(BeanUtils.sumBigDecimalProperty(divideInLayers, CgnciReinsuranceRiskLayerVO::getNetPremium));
                    divideInNetPremium = divideInNetPremium.add(manageReportDO.getDivideInNetPremium());
                }

                // 临分分出信息
                if (VerifyUtils.isNotEmpty(lfDivideOutReinsurance)) {
                    List<CgnciReinsuranceRiskVO> lfDivideOutRisks = lfDivideOutReinsurance.getRisks();
                    CgnciReinsuranceRiskVO lfDivideOutRisk = lfDivideOutRisks.stream().filter(value -> risk.getRiskSubTypeCode().equals(value.getRiskSubTypeCode())).findFirst().orElse(null);
                    if (VerifyUtils.isNotEmpty(lfDivideOutRisk)) {
                        // 获取当前险种所有标的的险种层信息
                        List<CgnciReinsuranceRiskInsuredVO> riskInsureds1 = lfDivideOutRisk.getRiskInsureds();
                        List<CgnciReinsuranceRiskLayerVO> lfDivideOutLayers = riskInsureds1.stream().map(CgnciReinsuranceRiskInsuredVO::getRiskLayers).flatMap(List::stream).collect(Collectors.toList());
                        if (VerifyUtils.isNotEmpty(lfDivideOutLayers)) {
                            // 分出毛保费
                            manageReportDO.setDivideOutGrossPremium(BeanUtils.sumBigDecimalProperty(lfDivideOutLayers, CgnciReinsuranceRiskLayerVO::getGrossPremium));
                            divideOutGrossPremium = divideOutGrossPremium.add(manageReportDO.getDivideOutGrossPremium());
                            // 分期自保分出(分出保费)
                            /*manageReportDO.setStageDivideOutGrossPremium(null);
                            stageDivideOutGrossPremium = stageDivideOutGrossPremium.add(manageReportDO.getStageDivideOutGrossPremium());*/
                        }
                    }
                }

                // 合约分出信息
                if (VerifyUtils.isNotEmpty(hyDivideOutReinsuranceList)) {
                    for (CgnciReinsuranceInfoVO hyDivideOut : hyDivideOutReinsuranceList) {
                        List<CgnciReinsuranceRiskVO> hyDivideOutRisks = hyDivideOut.getRisks();
                        CgnciReinsuranceRiskVO hyDivideOutRisk = hyDivideOutRisks.stream().filter(value -> risk.getRiskSubTypeCode().equals(value.getRiskSubTypeCode())).findFirst().orElse(null);
                        if (VerifyUtils.isNotEmpty(hyDivideOutRisk)) {
                            // 获取当前险种所有标的的险种层信息
                            List<CgnciReinsuranceRiskInsuredVO> riskInsureds3 = hyDivideOutRisk.getRiskInsureds();
                            List<CgnciReinsuranceRiskLayerVO> hyDivideOutLayers = riskInsureds3.stream().map(CgnciReinsuranceRiskInsuredVO::getRiskLayers).flatMap(List::stream).collect(Collectors.toList());
                            if (VerifyUtils.isNotEmpty(hyDivideOutLayers)) {
                                // 分出毛保费
                                manageReportDO.setDivideOutGrossPremium(VerifyUtils.isEmpty(manageReportDO.getDivideOutGrossPremium()) ? BigDecimal.ZERO : manageReportDO.getDivideOutGrossPremium()
                                        .add(BeanUtils.sumBigDecimalProperty(hyDivideOutLayers, CgnciReinsuranceRiskLayerVO::getEstimatedPremium)));
                                divideOutGrossPremium = divideOutGrossPremium.add(BeanUtils.sumBigDecimalProperty(hyDivideOutLayers, CgnciReinsuranceRiskLayerVO::getEstimatedPremium));
                            }
                        }
                    }
                }


                // 自保净自留 = 自保分入-自保分出
                manageReportDO.setNetSelfShare(manageReportDO.getDivideInGrossPremium().subtract(manageReportDO.getDivideOutGrossPremium()));
                netSelfShare = netSelfShare.add(manageReportDO.getNetSelfShare());
                // 净自留占原保费比例 = 自保净自留/保费
                manageReportDO.setNetSelfShareRate(manageReportDO.getNetSelfShare().divide(manageReportDO.getPremiumInclTax(), 2, RoundingMode.HALF_UP));
                netSelfShareRate = netSelfShareRate.add(manageReportDO.getNetSelfShareRate());
                // 排序字段
                manageReportDO.setSort(sort.incrementAndGet());
                manageReportList.add(manageReportDO);
            }

            // 记录该保单的一条汇总记录
            CgnciManageReportDO totalManageReportDO = new CgnciManageReportDO();
            totalManageReportDO.setRegisterId(extractDTO.getRegisterId());
            totalManageReportDO.setInsuranceId(insurance.getInsuranceId());
            totalManageReportDO.setProjectId(insurance.getProjectId());
            totalManageReportDO.setProjectName(insurance.getProjectName());
            totalManageReportDO.setProjectNo(insurance.getProjectNo());
            totalManageReportDO.setBusinessYear(insurance.getBusinessYear());
            totalManageReportDO.setRiskSubType("汇总");
            totalManageReportDO.setRiskSubTypeCode("total");
            // 取直保保额
            totalManageReportDO.setMaxCompensation(maxCompensation);
            // 取直保保费(含税)
            totalManageReportDO.setPremiumInclTax(premiumInclTax);
            // 费率= 保费/保额
            totalManageReportDO.setFeeRate(feeRate);
            // 分入毛保费
            totalManageReportDO.setDivideInGrossPremium(divideInGrossPremium);
            // 分入手续费
            totalManageReportDO.setDivideInCommission(divideInCommission);
            // 分入净保费
            totalManageReportDO.setDivideInNetPremium(divideInNetPremium);
            // 分出毛保费
            totalManageReportDO.setDivideOutGrossPremium(divideOutGrossPremium);
            // 分期自保分出(分出保费)
            //totalManageReportDO.setStageDivideOutGrossPremium(stageDivideOutGrossPremium);
            // 自保净自留 = 自保分入-自保分出
            totalManageReportDO.setNetSelfShare(netSelfShare);
            // 净自留占原保费比例 = 自保净自留/保费
            totalManageReportDO.setNetSelfShareRate(netSelfShareRate);
            totalManageReportDO.setSort(sort.incrementAndGet());
            manageReportList.add(totalManageReportDO);
        });

        this.saveBatch(manageReportList);
    }

    @Override
    public ExcelWriter exprotManageReport(Long registerId) {
        List<CgnciManageReportDO> list = this.list(new LambdaQueryWrapper<CgnciManageReportDO>()
                .eq(CgnciManageReportDO::getRegisterId, registerId));
        ExcelWriter writer = ExcelUtil.getWriter(true);
        Sheet sheet = writer.getSheet();
        // 冻结 参数分别为：列数、行数、左侧列索引、顶部行索引
        sheet.createFreezePane(3, 1, 3, 1);
        try {
            List<String> title = CollUtil.newArrayList(
                    "项目名称",
                    "项目年度",
                    "险种",
                    "保额",
                    "费率",
                    "保费",
                    "自保分入",
                    "分期自保分入",
                    "分入手续费",
                    "分期分入手续费",
                    "自保净分入",
                    "分期自保净分入",
                    "自保分出",
                    "分期自保分出",
                    "自保净自留",
                    "净自留占原保费比例");
            writer.writeHeadRow(title);

            // 偏移量
            AtomicReference<Integer> offset = new AtomicReference<>(0);

            // 根据保单id分组
            Map<Long, List<CgnciManageReportDO>> manageReportMap = list.stream().collect(Collectors.groupingBy(CgnciManageReportDO::getInsuranceId));
            // 循环manageReportMap
            manageReportMap.forEach((insuranceId, manageReportList) -> {
                // 根据排序字段降序排序
                manageReportList = manageReportList.stream().sorted(Comparator.comparingInt(CgnciManageReportDO::getSort).reversed()).collect(Collectors.toList());
                // 记录listSize
                manageReportList.forEach(manage -> writer.writeRow(Arrays.asList(
                        manage.getProjectName(),
                        manage.getBusinessYear(),
                        manage.getRiskSubType(),
                        manage.getMaxCompensation(),
                        manage.getFeeRate().multiply(new BigDecimal("100")) + "%",
                        manage.getPremiumInclTax(),
                        manage.getDivideInGrossPremium(),
                        manage.getStageDivideInGrossPremium(),
                        manage.getDivideInCommission(),
                        manage.getStageDivideInCommission(),
                        manage.getDivideInNetPremium(),
                        manage.getStageDivideInNetPremium(),
                        manage.getDivideOutGrossPremium(),
                        manage.getStageDivideOutGrossPremium(),
                        manage.getNetSelfShare(),
                        manage.getNetSelfShareRate())));

                // 合并单元格
                writer.merge(offset.get() + 1, offset.get() + manageReportList.size(), 0, 0, manageReportList.get(0).getProjectName(), false);
                writer.merge(offset.get() + 1, offset.get() + manageReportList.size(), 1, 1, manageReportList.get(0).getBusinessYear(), false);
                offset.getAndSet(offset.get() + manageReportList.size());

            });
        } catch (Exception e) {
            log.error("导出汇总失败", e);
            throw new GlobalException(ResponseCodeEnum.PARAMS_ILLEGAL, "导出汇总失败");
        }
        return writer;
    }
}
